(define-module (grump cli)
  #:use-module (grump utils)
  #:use-module (srfi srfi-1)
  #:use-module (srfi srfi-9)
  #:use-module (srfi srfi-11)
  #:use-module (srfi srfi-71)
  #:use-module (ice-9 match))

(define-record-type <cli>
  (make-cli program groups args)
  cli?
  (program cli-program set-cli-program!)
  (groups  cli-groups  set-cli-groups!)
  (args    cli-args    set-cli-args!))

(define-record-type <group>
  (make-group type flags)
  group?
  (type  group-type)
  (flags group-flags set-group-flags!))

(define-record-type <flag>
  (make-flag params)
  flag?
  (params flag-params))

(define (add-cli-flags! cli type flags)
  (let ((group (make-group type '())))
    (for-each
     (lambda (params)
       (let ((flag (make-flag params)))
         (set-group-flags!
          group (append! (group-flags group)
                         (list flag)))))
     flags)
    (set-cli-groups!
     cli (append! (cli-groups cli)
                  (list group)))))

(define-public (parse-cli-spec cli-spec)
  (let ((cli (make-cli #f '() '())))
    (for-each-keyword
     (match-lambda*
       ((#f params)
        (debug! "params: ~s" params))
       ((#:program (name))
        (set-cli-program! cli name))
       ((#:once-each flags)
        (add-cli-flags! cli 'once-each flags))
       ((#:once-any flags)
        (add-cli-flags! cli 'once-any flags))
       ((#:multi flags)
        (add-cli-flags! cli 'multi flags))
       ((#:args args)
        (set-cli-args! cli args)))
     cli-spec)
    cli))

(define (for-each-keyword proc lst)
  "Call `(proc key vals)' for each sublist that starts with a keyword.

Elements at the beginning which don't have a keyword in front are
passed as `(proc #f vals)' before processing the keywords."
  (let-values (((vals lst) (break keyword? lst)))
    (unless (null? vals)
      (proc #f vals))
    (let loop ((lst lst))
      (unless (null? lst)
        (let*-values (((key rest) (uncons lst))
                      ((vals rest) (break keyword? rest)))
          (proc key vals)
          (loop rest))))))

(define (split-keywords lst)
  (let ((result '()))
    (for-each-keyword
     (lambda (key vals)
       (set! result
         (append! result (list (cons key vals)))))
     lst)
    result))
